package com.zrzhen.springbootdemo.common.http.okhttp;

import com.zrzhen.springbootdemo.common.http.Ipphttp;
import com.zrzhen.springbootdemo.common.util.json.JsonUtil;
import okhttp3.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.net.ssl.*;
import java.io.File;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class Okhttp implements Ipphttp {

    private static final Logger logger = LoggerFactory.getLogger(Okhttp.class);

    public static final int DEFAULT_CONNECTION_TIMEOUT = 60;
    public static final int DEFAULT_READ_TIMEOUT = 60;
    public static final int DEFAULT_WRIETE_TIMEOUT = 60;

    private volatile static OkHttpClient client;
    private volatile static OkHttpClient clientHttps;


    /**
     * 获取单例OkHttpClient
     * @param request
     * @return
     */
    public static OkHttpClient getClient(Request request) {
        if (request.url().isHttps()) {
            if (clientHttps == null) {
                synchronized (Okhttp.class) {
                    if (clientHttps == null) {
                        clientHttps = (new OkHttpClient.Builder())
                                .retryOnConnectionFailure(true)
                                .connectTimeout(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                                .writeTimeout(DEFAULT_WRIETE_TIMEOUT, TimeUnit.SECONDS)
                                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                                .hostnameVerifier(new HostnameVerifier() {
                                    @Override
                                    public boolean verify(String hostname, SSLSession session) {
                                        return true;
                                    }
                                })
                                .build();
                    }
                }
            }
            return clientHttps;
        } else {
            if (client == null) {
                synchronized (Okhttp.class) {
                    if (client == null) {
                        client = (new OkHttpClient.Builder())
                                .connectTimeout(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
                                .readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
                                .writeTimeout(DEFAULT_WRIETE_TIMEOUT, TimeUnit.SECONDS)
                                .retryOnConnectionFailure(true)
                                .build();
                    }
                }
            }
            return client;
        }
    }

    private Okhttp() {
    }

    @Override
    public String get(String url, Map<String, String> params, Map<String, String> headers) {
        String urlWithParams = url + addParms(params);
        Request request = new Request.Builder()
                .url(urlWithParams)
                .headers(getHeaders(headers))
                .build();
        return httpResult(request);
    }

    @Override
    public String postJson(String url, String jsonParams, Map<String, String> headers) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
        Request request = new Request.Builder()
                .url(url)
                .headers(getHeaders(headers))
                .post(requestBody)
                .build();
        return httpResult(request);
    }

    @Override
    public String postJsonMap(String url, Map<String, Object> params, Map<String, String> headers) {
        String jsonParams = JsonUtil.map2Json(params);
        return postJson(url, jsonParams, headers);
    }

    @Override
    public String postForm(String url, Map<String, String> params, Map<String, String> headers) {
        FormBody.Builder builder = new FormBody.Builder();
        if (null != mapWithoutNull(params)) {
            params.forEach((k, v) -> builder.add(k, v));
        }
        Request request = new Request.Builder()
                .url(url)
                .headers(getHeaders(headers))
                .post(builder.build())
                .build();
        return httpResult(request);
    }

    @Override
    public String postXmlParams(String url, String xml, Map<String, String> headers) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);
        Request request = new Request.Builder()
                .url(url)
                .headers(getHeaders(headers))
                .post(requestBody)
                .build();
        return httpResult(request);
    }

    @Override
    public String postFile(String url, File file, Map<String, String> headers) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), file);
        Request request = new Request.Builder()
                .url(url)
                .headers(getHeaders(headers))
                .post(requestBody)
                .build();
        return httpResult(request);
    }


    /**
     * 拼接头信息
     * @param headers
     * @return
     */
    private static Headers getHeaders(Map<String, String> headers) {
        Headers.Builder headerBuilder = new Headers.Builder();
        headerBuilder.add("Connection", "close");
        if (null != mapWithoutNull(headers)) {
            headers.forEach(((k, v) -> headerBuilder.add(k, v)));
        }
        Headers headers1 = headerBuilder.build();
        return headers1;
    }

    /**
     * 返回相应结果
     * @param request
     * @return
     */
    private static String httpResult(Request request) {
        String resBody = null;
        Response response = null;
        try {
            response = getClient(request).newCall(request).execute();
            resBody = response.body().string();
        } catch (Exception e) {
            logger.error("okhttp3 error >> ex = {}", ExceptionUtils.getStackTrace(e));
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return resBody;
    }

    /**
     * 删除key或value为null的键值
     *
     * @param params
     * @return
     */
    private static Map<String, String> mapWithoutNull(Map<String, String> params) {
        if (params != null && params.keySet().size() > 0) {
            Iterator<Map.Entry<String, String>> iterator = params.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                if (null == entry.getKey() || null == entry.getValue()) {
                    iterator.remove();
                }
            }
        }
        return params;
    }

    /**
     * 拼接URL上的参数
     *
     * @param params
     * @return
     */
    private static String addParms(Map<String, String> params) {
        StringBuffer sb = new StringBuffer();
        if (params != null && params.keySet().size() > 0) {
            boolean firstFlag = true;
            Iterator iterator = params.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry<String, String>) iterator.next();
                if (firstFlag) {
                    sb.append("?" + entry.getKey() + "=" + entry.getValue());
                    firstFlag = false;
                } else {
                    sb.append("&" + entry.getKey() + "=" + entry.getValue());
                }
            }
        }
        return sb.toString();
    }

    private static X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
    }

    private static SSLSocketFactory sslSocketFactory() {
        try {
            //信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }


    private static ConnectionPool pool() {
        //maxIdleConnections;每个connection的空闲socket连接数目；
        //keepAliveDurationNs;每个空闲socket连接的keep-alive时长；
        return new ConnectionPool(100, 50, TimeUnit.MINUTES);
    }


}
